/* Turska Framework/UI library (Turs2)
 * Copyright 2003-2010 Jetro Lauha
 * All rights reserved.
 * Web: http://iki.fi/jetro/turska/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of EITHER:
 *   (1) The GNU Lesser General Public License as published by the Free
 *       Software Foundation; either version 2.1 of the License, or (at
 *       your option) any later version. The text of the GNU Lesser
 *       General Public License is included with this library in the
 *       file LICENSE-LGPL.txt.
 *   (2) The BSD-style license that is included with this library in
 *       the file LICENSE-BSD.txt.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
 * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
 *
 * $Id: T2ImageOpenGL.cpp 202 2012-03-23 12:42:59Z jlauha@gmail.com $
 * $Revision: 202 $
 */

#include <string.h>
#include <math.h>
#include "Turs2.h"

#if defined(T2_USE_SDLOPENGL) || defined(T2_USE_OPENGL) || defined(T2_USE_OPENGL_ES)

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif // _WIN32

#ifdef T2_USE_OPENGL

#ifdef __APPLE__
    #include <OpenGL/gl.h>
    #include <OpenGL/glu.h>
#else
    #include "GL/gl.h"
    #include "GL/glu.h"
#endif

#else // T2_USE_OPENGL_ES:
    #ifdef T2_OPENGL_ES_IPHONE
        #include <OpenGLES/ES1/gl.h>
        #include <OpenGLES/ES1/glext.h>
    #else // T2_OPENGL_ES_GENERIC:
        #include <GLES/gl.h>
        #include <GLES/glext.h>
    #endif
#endif


namespace turska
{


ImageOpenGL::ImageOpenGL(GraphicsOpenGL *graphics, UI32 width, UI32 height, UI32 flags,
                         UI32 glId, const F32 *uv, bool takeTexOwnership) :
    Image(width, height, IDM_NORMAL, flags),
    mGraphics(graphics),
    mGLId(glId),
    mHalfWidth((F32)width / 2.0f),
    mHalfHeight((F32)height / 2.0f),
    mTargetWidth((F32)width),
    mTargetHeight((F32)height),
    mAngle(0),
    mScaleX(1), mScaleY(1),
    mUsingExplicitTargetSize(false),
    mOwnsTextureGLId(takeTexOwnership)
{
    UI32 a;
    for (a = 0; a < 4; ++a)
        mUV[a] = uv[a];
    mColor.set(0xffffffff);
}


ImageOpenGL::~ImageOpenGL()
{
    if (mOwnsTextureGLId)
        glDeleteTextures(1, (const GLuint *)&mGLId);
}


void ImageOpenGL::draw(IMAGE_DRAW_MODE drawMode, UI32 flags, F32 x, F32 y, const UI8 *color, const Rectangle *sourceRect)
{
    bool usingSrcArea = true;
    Rectangle srcArea;
    if (sourceRect)
        srcArea = *sourceRect;
    else if (!sourceRect && mUsingSourceRectangle)
        srcArea = mSourceRectangle;
    else
    {
        srcArea = Rectangle(0, 0, (TREAL)mWidth, (TREAL)mHeight);
        usingSrcArea = false;
    }

    F32 positions[8]; // 4x x,y
    F32 texCoords[8]; // 4x u,v
    UI8 colors[16];   // 4x r,g,b,a

    if (flags & IF_COLOR)
    {
        for (I32 o = 0; o < 16; )
        {
            colors[o++] = color[0];
            colors[o++] = color[1];
            colors[o++] = color[2];
            colors[o++] = (UI8)0xff;
        }
    }
    else
        memset(colors, 0xff, 16);
    if (flags & IF_ALPHA)
        colors[3] = colors[7] = colors[11] = colors[15] = color[3];

    F32 uv[4]; // temp for top-left and bottom-right corner uvs
    memcpy(uv, mUV, 4 * sizeof(F32));
    F32 halfWidth = mHalfWidth, halfHeight = mHalfHeight;
    if (usingSrcArea)
    {
        F32 uvW = uv[2] - uv[0];
        F32 uvH = uv[3] - uv[1];
        uv[0] += uvW * srcArea.x / mWidth;
        uv[1] += uvH * srcArea.y / mHeight;
        uv[2] = uv[0] + uvW * srcArea.width / mWidth;
        uv[3] = uv[1] + uvH * srcArea.height / mHeight;
        halfWidth = srcArea.width * 0.5f;
        halfHeight = srcArea.height * 0.5f;
    }
    if (flags & IF_FLIP_HORIZ)
    {
        float t = uv[0];
        uv[0] = uv[2];
        uv[2] = uv[0];
    }
    if (flags & IF_FLIP_VERT)
    {
        float t = uv[1];
        uv[1] = uv[3];
        uv[3] = t;
    }

    texCoords[0] = uv[0];
    texCoords[1] = uv[1];
    texCoords[2] = uv[2];
    texCoords[3] = uv[1];
    texCoords[4] = uv[0];
    texCoords[5] = uv[3];
    texCoords[6] = uv[2];
    texCoords[7] = uv[3];
    
    if (flags & IF_TRANSFORM)
    {
        F32 pivotX = -halfWidth, pivotY = -halfHeight; // transform center adjusted to top-left (default)
        if (flags & IF_ALIGN_RIGHT)
            pivotX = halfWidth;
        if (flags & IF_ALIGN_HORIZ_CENTER)
            pivotX = (mUsingCustomPivot ? mPivotX : halfWidth) - halfWidth;
        if (flags & IF_ALIGN_BOTTOM)
            pivotY = halfHeight;
        if (flags & IF_ALIGN_VERT_CENTER)
            pivotY = (mUsingCustomPivot ? mPivotY : halfHeight) - halfHeight;

        F32 vx1[4], vy1[4], vx2[4], vy2[4];
        F32 *vx = vx1, *vy = vy1;
        vx[0] = -halfWidth - pivotX; vy[0] = -halfHeight - pivotY;
        vx[1] = halfWidth - pivotX; vy[1] = -halfHeight - pivotY;
        vx[2] = -halfWidth - pivotX; vy[2] = halfHeight - pivotY;
        vx[3] = halfWidth - pivotX; vy[3] = halfHeight - pivotY;
        if (flags & IF_SCALE)
        {
            vx[0] *= mScaleX; vy[0] *= mScaleY;
            vx[1] *= mScaleX; vy[1] *= mScaleY;
            vx[2] *= mScaleX; vy[2] *= mScaleY;
            vx[3] *= mScaleX; vy[3] *= mScaleY;
        }
        if (flags & IF_ROTATE)
        {
            F32 c = cosf(mAngle), s = sinf(mAngle);
            // rotate current vertices to temp vertices
            vx2[0] = vx[0] * c - vy[0] * s; vy2[0] = vx[0] * s + vy[0] * c;
            vx2[1] = vx[1] * c - vy[1] * s; vy2[1] = vx[1] * s + vy[1] * c;
            vx2[2] = vx[2] * c - vy[2] * s; vy2[2] = vx[2] * s + vy[2] * c;
            vx2[3] = vx[3] * c - vy[3] * s; vy2[3] = vx[3] * s + vy[3] * c;
            // switch temp vertices to final ones
            vx = vx2; vy = vy2;
        }

        positions[0] = vx[0] + x;
        positions[1] = vy[0] + y;
        positions[2] = vx[1] + x;
        positions[3] = vy[1] + y;
        positions[4] = vx[2] + x;
        positions[5] = vy[2] + y;
        positions[6] = vx[3] + x;
        positions[7] = vy[3] + y;
        if (flags & IF_FORCE_INT)
        {
            for (int a = 0; a < 8; ++a)
                positions[a] = ::floorf(positions[a]);
        }
    }
    else
    {
        F32 targetWidth = mTargetWidth, targetHeight = mTargetHeight;
        if (usingSrcArea && !mUsingExplicitTargetSize)
        {
            targetWidth = srcArea.width;
            targetHeight = srcArea.height;
        }
        F32 topLeftX = x, topLeftY = y; // top-left (default)
        if (flags & IF_ALIGN_RIGHT)
            topLeftX -= targetWidth;
        if (flags & IF_ALIGN_HORIZ_CENTER)
        {
            const F32 normalized = ((mUsingCustomPivot ? mPivotX : halfWidth) - halfWidth) / halfWidth; // range -1..1
            const F32 halfTargetWidth = targetWidth * 0.5f;
            topLeftX -= halfTargetWidth + halfTargetWidth * normalized;
        }
        if (flags & IF_ALIGN_BOTTOM)
            topLeftY -= targetHeight;
        if (flags & IF_ALIGN_VERT_CENTER)
        {
            const F32 normalized = ((mUsingCustomPivot ? mPivotY : halfHeight) - halfHeight) / halfHeight; // range -1..1
            const F32 halfTargetHeight = targetHeight * 0.5f;
            topLeftY -= halfTargetHeight + halfTargetHeight * normalized;
        }

        positions[0] = topLeftX;
        positions[1] = topLeftY;
        positions[2] = topLeftX + targetWidth;
        positions[3] = topLeftY;
        positions[4] = topLeftX;
        positions[5] = topLeftY + targetHeight;
        positions[6] = topLeftX + targetWidth;
        positions[7] = topLeftY + targetHeight;
        if (flags & IF_FORCE_INT)
        {
            for (int a = 0; a < 8; ++a)
                positions[a] = ::floorf(positions[a]);
        }
    }

    mGraphics->batchImageDraw(mGLId, drawMode, positions, texCoords, colors);
}


} // namespace turska
#endif // T2_USE_SDLOPENGL
